import axios from 'axios'
import { User } from '../models'
import redis from '../lib/redis'
import CONFIG from '../config'
import { aesDecrypt, sha1 } from '../lib/helper'
import moment from 'moment'
import * as $user from './user'

/**
 * get access_token from wechat
 * Called every 7200s
 * @return {string} access_token
 */
export const getAccessToken = async () => {
  console.info('getAccessToken')
  let wechatToken = await redis.getAsync('wechatToken')

  if (wechatToken) {
    wechatToken = JSON.parse(wechatToken)
    let expireTime = moment(wechatToken.expire_time)
    if (expireTime.isAfter()) {
      return wechatToken.access_token
    }
  }
  wechatToken = await refreshAccessToken()
  return wechatToken.access_token
}

/**
 * get access_token from wechat
 * Called every 7200s
 * @return {object} access_token
 */
export const refreshAccessToken = async () => {
  console.info('refreshAccessToken')
  // Get access_token from wechat web service
  let wechatToken = {}
  console.log(CONFIG.wechat)
  const { appId, appSecret } = CONFIG.wechat[CONFIG.wechat.defaultAppId]
  let res = null
  try {
    res = await axios.get('https://api.weixin.qq.com/cgi-bin/token', {
      params: {
        appid: appId,
        secret: appSecret,
        grant_type: 'client_credential'
      }
    })
  } catch (e) {
    console.error(e)
  }
  if (res) {
    wechatToken = {
      access_token: res.data.access_token,
      expire_time: moment().add(res.data.expires_in - 120, 's')
    }
    console.log(res.data)
    await redis.setAsync(
      'wechatToken',
      JSON.stringify(wechatToken),
      'EX',
      (res.data.expires_in - 120) * 1000
    )
  }
  // schedule a timer to refresh access_token
  setTimeout(refreshAccessToken, (res.data.expires_in - 60) * 1000)
  return wechatToken
}

/**
 * get template list from wechat
 * @return {string, user}
 */
export const getTemplateList = async () => {
  const accessToken = await getAccessToken()
  let res = await axios.post(
    `https://api.weixin.qq.com/cgi-bin/wxopen/template/list?access_token=${accessToken}`,
    {
      offset: 0,
      count: 20
    }
  )
  if (res && res.data) {
    if (res.data.errcode == 0) {
      console.log(res.data)
      WECHAT.templates = res.data.list.reduce((map, obj) => {
        map[obj.title] = obj
        return map
      }, {})
    } else if (res.data.errcode === 40001) {
      // error happened, refresh token and do again
      await refreshAccessToken()
      setTimeout(getTemplateList)
    }
  }
  return res
}

/**
 * send template message to a user
 * @param {string} appid
 * @param {string} appsecret
 * @return {string, user}
 */
export const sendTemplateMessage = async (
  openId,
  formId,
  templateId,
  page = 'index',
  data,
  emphasisKeyword
) => {
  const body = {
    touser: openId,
    template_id: templateId,
    page: page,
    form_id: formId,
    data,
    emphasis_keyword: emphasisKeyword
  }
  const accessToken = await getAccessToken()
  let response = await axios.post(
    `https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=${accessToken}`,
    body
  )
  // handle success
  let res = response.data
  // invalid credential, access_token is invalid or not latest
  if (res.errcode === 40001) {
    await refreshAccessToken()
    await sendTemplateMessage(
      openId,
      formId,
      templateId,
      page,
      data,
      emphasisKeyword
    )
  }
  return res
}

/**
 * get session key using code
 * @param {string} appid
 * @param {string} appsecret
 * @param {string} code
 * @return {string, user}
 */
export const getSessionKey = async (code, clientAppId) => {
  let config = null
  if (clientAppId) {
    config = CONFIG.wechat[clientAppId]
  }
  if (!config) {
    config = CONFIG.wechat[CONFIG.wechat.defaultAppId]
  }
  const { appId, appSecret } = config
  let res = await axios.get('https://api.weixin.qq.com/sns/jscode2session', {
    params: {
      appid: appId,
      secret: appSecret,
      js_code: code,
      grant_type: 'authorization_code'
    }
  })
  if (res.data.errcode) {
    throw new Error(
      `${ERRORS.ERR_GET_SESSION_KEY}\n${res.data.errcode}\n${res.data.errmsg}`
    )
  }
  return res.data
}

/**
 * Get user token using code from weixin, then store session_key and openid in database
 * @param {string} code
 * @return jwtToken
 */
export const wxLogin = async (code, appId) => {
  const res = await getSessionKey(code, appId)
  const { openid, unionid, session_key } = res
  return await $user.wxLogin(openid, session_key, unionid)
}

/**
 * decrypt data using stored session key or code
 * @param {string} encryptedData
 * @param {string} iv
 * @param {json} input
 * @return {string, user}
 */
export const decryptInfo = async (encryptedData, iv, userId) => {
  let sessionKey = null
  if (userId) {
    const user = await User.findById(userId)
    if (user == null) {
      throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}\n${userId}`)
    }
    sessionKey = user.accessToken
  }
  if (sessionKey == null) {
    throw new Error(`${ERRORS.ERR_GET_SESSION_KEY}\n${code}\n${userId}`)
  }
  // 生成 3rd_session
  const skey = sha1(sessionKey)

  // 解密数据
  let decryptedData
  try {
    decryptedData = aesDecrypt(sessionKey, iv, encryptedData)
    decryptedData = JSON.parse(decryptedData)
  } catch (e) {
    // console.debug('Auth: %s: %o', ERRORS.ERR_IN_DECRYPT_DATA, e)
    throw new Error(`${ERRORS.ERR_IN_DECRYPT_DATA}\n${e}`)
  }
  return decryptedData
}
